/*
 * Scp1000GLIB05.pde
 *
 * Read the Barometric Pressure and Temperature values from SCP1000 sensor
 * in every 30 minutes and send data to Serial.
 * Now support 4 sevent-segment LED to display current senasor data
 * (temperature and barometric pressure in hPa).
 * Data is stored in EEPROM and send all data from latest to oldest
 * via serial port on startup.
 * 
 * based on sample code from Arduino forum
 * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1236443720/8
 *
 * HISTORY
 * 
 * 2010/7/7 change interval (30min)
 * 2010/7/29 add 7 segments display support
 *           now we use 2 byte for one sensor data 
 * 2010/8/3  add function to display temprature
 * 2010/8/6  internal timer control function was replaced by Metro library.
 * 2010/8/23 modify comment
 * 2010/8/31 disable recording on startup
 *
 * LIBRARY
 * Arduino: Playground - Metro
 * http://www.arduino.cc/playground/Code/Metro
 *
 * Arduino Forum > Hardware > Interfacing > How to connect SCP1000 pressure sensor
 * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1236443720/8
 *
 */

/*
 * Copyright (c) 2010 elekid
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

#include <Metro.h>
#include <EEPROM.h>
#include <SCP1000.h>

// Specify slave select pin for SCP1000 device
#define SELECT_PIN 10

#define LED_PIN1 14
#define NUM_DATA 25

// precision of data stored in EEPROM ( 0.01 step)
#define EEPROM_BARO_PRECISION   100.0
// differnce of following value will be save (in above precision)
#define EEPROM_MIN_BARO         900
// number of data stored in EEPROM (EEPROM is 512 byte only)
#define EEPROM_MAX_DATA         250
// data will be stored from following address
#define EEPROM_MIN_ADDR          4
#define EEPROM_MAX_ADDR    EEPROM_MIN_ADDR + EEPROM_MAX_DATA*2 -1

// addr 0 and 1 will be used 
#define EEPROM_ADDR_STORED   0
// addr 2 and 3 will be used 
#define EEPROM_CNT_STORED    2

#define GRAPH_CNT 8

// for 7 segments x 4 display
int CLOCK_PIN =2;
int DATA_PIN  =3;
int TR[4] = {
  16,17,18,19,};

//  
//   aaaaaaaa
//  f        b
//  f        b
//  f        b
//   gggggggg
//  e        c
//  e        c
//  e        c
//   dddddddd     DP
//
//  abcdefgD
// B00000000
//
// definition of 7 segment patterns
byte NUM[13] = {
  B00000011,  // 0
  B10011111,  // 1
  B00100101,  // 2
  B00001101,  // 3
  B10011001,  // 4
  B01001001,  // 5
  B11000001,  // 6
  B00011111,  // 7 
  B00000001,  // 8
  B00011001,  // 9
  B11111111,  // all off
  B11100101,  // "c" for celcius
  B11111101,  // "-" for minus sign
};

#define MINUS_SIGN 12
#define CELCIUS 11
#define ALL_OFF 10

// loop() counter for switching 7seg LED
#define SWITCH_SEG_PERIOD 300
unsigned long switchSegCounter=SWITCH_SEG_PERIOD;

// 1806832UL (milsec) is 30 minutes
#define DATA_SEND_PERIOD 1800262UL

#define SWITCH_DISP_PERIOD 4000
#define SWITCH_HPA_PERIOD  2000
#define MEASURE_ONLY_PERIOD 120000UL

byte SEG[4];
int currentSegID =3 ;

byte TempOrBaroFlg=0;


SCP1000 scp1000(SELECT_PIN);

// Timer
Metro dataSendTimer = Metro(DATA_SEND_PERIOD);
Metro switchDispTimer = Metro(SWITCH_DISP_PERIOD);
Metro measureOnlyTimer = Metro(MEASURE_ONLY_PERIOD);


// for trend chart display (future use)
unsigned int P[GRAPH_CNT] = {
  0,0,0,0,0,0,0,0,
};

unsigned int currentBaro = 0;
unsigned int currentTemp = 0;


void setup()
{

  Serial.begin(9600); // Open serial connection to report values to host
  //Serial.println("Starting up");

  pinMode(LED_PIN1, OUTPUT);

  pinMode(CLOCK_PIN, OUTPUT);
  pinMode(DATA_PIN,  OUTPUT);
  for (int i = 0 ; i < 4 ; i++){
    pinMode(TR[i],  OUTPUT);    
    digitalWrite(TR[i], HIGH);
  }

  delay(1000);
  listEepromData();
  delay(1000);

  scp1000.init();
  delay(500);
  readSensor();
  // recordData();
  // sendData();

}

void loop()
{
  if ( dataSendTimer.check() == 1 ) {
    readSensor();
    recordData();
    sendData();
  } 
  else if ( measureOnlyTimer.check() == 1 ) {
    readSensor();
  } 
  else if ( switchDispTimer.check() == 1 ) {
    switchBaroTemp();
  }
  // update 7 segment LED
  displayNumbers();
}


void readSensor() {

  digitalWrite(LED_PIN1,HIGH);

  scp1000.readSensor();
  if (scp1000.BaroP < 800 ) {
    delay(20);
    scp1000.readSensor();
  }

  currentBaro = int(scp1000.BaroP + 0.5 ) ;
  currentTemp = scp1000.TempC ;
  /*
    shiftValues();
   P[GRAPH_CNT-1] = currentBaro;
   */

  TempOrBaroFlg = 0;
  extractBaroDigit();
  digitalWrite(LED_PIN1,LOW);
}

void sendData() {
  Serial.print(scp1000.BaroP);
  Serial.print(" hPa ");
  Serial.print(scp1000.TempC);
  Serial.println(" C");
}


void recordData() {
  // get current EEPROM address
  int addr = getEepromInfo(EEPROM_ADDR_STORED);
  // get current number of data in EEPROM
  int cnt =  getEepromInfo(EEPROM_CNT_STORED);

  int new_addr =0;
  int new_cnt = 0;

  // very first time
  if (addr == 0) { 
    new_addr = EEPROM_MIN_ADDR;
    new_cnt = 1;
  } 
  else {
    new_addr = addr + 2 ;
    if (new_addr > EEPROM_MAX_ADDR ) {
      new_addr = EEPROM_MIN_ADDR;
    }
    new_cnt = cnt + 1;
    if (new_cnt > EEPROM_MAX_DATA) {
      new_cnt= EEPROM_MAX_DATA;
    }
  }
  int new_val=calcCompressedValue(scp1000.BaroP);
  putEepromInfo(new_addr, new_val);

  putEepromInfo(EEPROM_ADDR_STORED, new_addr);
  putEepromInfo(EEPROM_CNT_STORED, new_cnt);
}

void switchBaroTemp() {
  if ( TempOrBaroFlg == 0 ) {
    TempOrBaroFlg = 1;
    extractBaroDigit();
    switchDispTimer.interval(SWITCH_DISP_PERIOD);
  } 
  else if (TempOrBaroFlg == 1 ) {
    TempOrBaroFlg = 0 ;
    extractTempDigit();
    switchDispTimer.interval(SWITCH_DISP_PERIOD);
  }
  /*      
   } else if (TempOrBaroFlg == 1 ) {
   TempOrBaroFlg = 2;
   SEG[3]=B11010001;
   SEG[2]=B00110001;
   SEG[1]=B00010001;
   SEG[0]=B11111111;
   switchDispTimer.interval(SWITCH_HPA_PERIOD);
   */
}

void extractBaroDigit(){
  SEG[3] = currentBaro / 1000 ;
  SEG[2] = (currentBaro - (1000 * SEG[3])) / 100 ;
  SEG[1] = (currentBaro - (1000 * SEG[3]) - (100 * SEG[2])) / 10 ;
  SEG[0] = (currentBaro - (1000 * SEG[3]) - (100 * SEG[2]) - (10 * SEG[1]));
}

void extractTempDigit() {
  // bit complicated here. becase i want display 24c instead of 024c 
  // when temperature is 24 degrees celcius
  if (currentTemp < 0 ) {
    SEG[3] = MINUS_SIGN;
  } 
  else { 
    SEG[3] = ALL_OFF;
  }
  if (  abs (currentTemp ) >= 100 ){
    SEG[2] = 9;
    SEG[1] = 9;
  } 
  else {
    if ( abs (currentTemp) >= 10 ){
      SEG[2] = currentTemp / 10 ;
      SEG[1] = abs (currentTemp) - SEG[2]*10;
    } 
    else { 
      SEG[2] = ALL_OFF;
      SEG[1] = abs (currentTemp);
    }
  }
  SEG[0] = CELCIUS ;
}

void shiftValues() {
  for ( int i = 0 ; i < GRAPH_CNT-1 ; i++) {
    P[i] = P[i+1];
  }
}


int getEepromInfo(int addr){
  byte b1 = EEPROM.read(addr);
  byte b2 = EEPROM.read(addr+1);
  int b = b1 << 8 | b2 ;
  return(b);
}

void putEepromInfo(int addr , int value) {
  byte b = value >> 8 & 0x00FF;
  EEPROM.write(addr, b);
  b = value & 0x00FF;
  EEPROM.write(addr+1,b);

}

int calcCompressedValue(float baro) {
  long new_value_long = baro * EEPROM_BARO_PRECISION -  EEPROM_MIN_BARO * EEPROM_BARO_PRECISION;
  return (int)new_value_long;
}

float calcDecompressedValue(int b) {
  float f = b / EEPROM_BARO_PRECISION;
  f = f +  EEPROM_MIN_BARO ;
  return f;
}

void listEepromData(){
  // get current EEPROM address
  int addr = getEepromInfo(EEPROM_ADDR_STORED);
  // get current number of data in EEPROM
  int cnt =  getEepromInfo(EEPROM_CNT_STORED);

  if (cnt == 0 ) return;

  Serial.print("POST ");

  int pos=addr;
  for ( int i = 0 ; i < cnt ; i++) {
    float f = calcDecompressedValue( getEepromInfo(pos) );
    Serial.print(f);
    Serial.print(" ");
    pos = pos - 2 ; 
    if ( pos < EEPROM_MIN_ADDR) {
      pos = EEPROM_MAX_ADDR;
    }
  }
  Serial.println();

}

void displayNumbers(){
  switchSegCounter++ ;
  if ( switchSegCounter >  SWITCH_SEG_PERIOD ) {
    switchSegCounter=0;
    currentSegID++ ;
    if ( currentSegID > 3) {
      currentSegID = 0 ;
    }
    displayNumber(currentSegID);
  }
}

void displayNumber(int digit) {
  for (int i = 0 ; i < 4 ; i++){
    // turn off all LEDs
    digitalWrite(TR[i], HIGH);
  }
  // turn on single LED
  digitalWrite(TR[digit],LOW);
  shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, NUM[SEG[digit]]);
} 



